#include "estree.hpp"
#include "cpp-gen/FormalParser.h"
#include "dummy_node.h"
namespace cyclone::parser
{
    PositionAnnotation Node::findRuleStartAndEnd(ParserRuleContext *node)
    {
        auto start = node->start ? node->start->getStartIndex() : 0;
        ull end = 0;
        if (node->stop)
            end = node->stop->getStopIndex() + 1;
        else if (node->start)
            end = node->start->getStopIndex() + 1;
        return PositionAnnotation{start, end};
    };

    std::vector<std::shared_ptr<TemplateElement>> TemplateElement::formatTemplateElement(antlr4::ParserRuleContext *ctx)
    {
        std::vector<std::shared_ptr<TemplateElement>> templateElementCollection;
        if (!ctx || !dynamic_cast<FormalParser::TemplateLiteralContext *>(ctx))
            return templateElementCollection;
        auto stringLiteral = reinterpret_cast<FormalParser::TemplateLiteralContext *>(ctx);
        if (!stringLiteral)
            return templateElementCollection;
        std::vector<FormalParser::AtomTemplateStringContext *> atomTemplateStringContextList;
        std::vector<FormalParser::BraceTemplateStringContext *> braceTemplateStringList;

        for (const auto &ele : stringLiteral->templateStringAtom())
        {
            if (dynamic_cast<FormalParser::AtomTemplateStringContext *>(ele))
                atomTemplateStringContextList.push_back(static_cast<FormalParser::AtomTemplateStringContext *>(ele));
            else
                braceTemplateStringList.push_back(static_cast<FormalParser::BraceTemplateStringContext *>(ele));
        }

        const auto completeText = stringLiteral->getText();
        const auto findBrace = braceTemplateStringList.size();

        static auto formartTemplateElement = [](ull start, ull end, const std::string &value, bool isTail)
        {
            auto element = std::make_shared<TemplateElement>(TemplateElement{start, end, TemplateElement::TemplateElementType, isTail, TemplateElementValue{value.size() ? std::optional<std::string>(value) : std::optional<std::string>(), value}});
            return element;
        };

        static const auto checkConnectedBrace = [](const decltype(braceTemplateStringList) &braceList, decltype(templateElementCollection) &resultList)
        {
            // let a = `a${name}${age}`
            if (braceList.size() < 2)
                return;
            for (auto i = 0; i < braceList.size() - 1; i++)
            {
                if (auto currentElementEnd = braceList.at(i)->getStop())
                    if (auto nextEleStart = braceList.at(i + 1)->getStart())
                    {
                        auto currentEleEnd = currentElementEnd->getStopIndex();
                        if (currentEleEnd + 1 == nextEleStart->getStartIndex())
                            resultList.push_back(formartTemplateElement(currentEleEnd + 1, currentEleEnd + 1, "", false));
                    }
            }
        };
        const auto originalRange = Node::findRuleStartAndEnd(stringLiteral);
        const auto templateStringStart = originalRange.start;
        const auto templateStringEnd = originalRange.end;

        if (!atomTemplateStringContextList.size())
        {
            if (findBrace)
            {
                templateElementCollection.push_back(formartTemplateElement(templateStringEnd, templateStringEnd, "", true));
                templateElementCollection.push_back(formartTemplateElement(templateStringStart, templateStringStart, "", false));
            }
            else
                templateElementCollection.push_back(formartTemplateElement(templateStringEnd - 1, templateStringEnd - 1, "", true));
            return templateElementCollection;
        }
        auto findTail = false;
        auto lookAtTokenStream = [&](int pos)
        {
            const auto exprectedPos = pos - templateStringStart;
            return exprectedPos < completeText.size() ? std::optional(completeText.at(exprectedPos)) : std::optional<char>();
        };
        {
            auto checkFirstTokenIsTemplateElement = [&]()
            {
                if (atomTemplateStringContextList.at(0)->TemplateStringAtom()->getSymbol()->getStartIndex() - 1 != stringLiteral->getStart()->getStartIndex())
                    templateElementCollection.push_back(formartTemplateElement(templateStringStart, templateStringStart, "", false));
            };
            std::string tempVale;
            auto elementStart = 0, elementEnd = 0;
            checkFirstTokenIsTemplateElement();
            checkConnectedBrace(braceTemplateStringList, templateElementCollection);
            for (const auto &singleAtomChar : atomTemplateStringContextList)
            {
                auto atomCharStartIndex = singleAtomChar->TemplateStringAtom()->getSymbol()->getStartIndex();

                if (!tempVale.size())
                    elementStart = atomCharStartIndex;
                tempVale += singleAtomChar->getText();
                const auto nextChar = lookAtTokenStream(atomCharStartIndex + 1);
                const auto nextTwoChar = lookAtTokenStream(atomCharStartIndex + 2);
                const auto nextTwoIsDollarLeftBrace = nextChar.has_value() && nextTwoChar.has_value() && nextChar.value() == TemplateElementSplitter::Dollar && nextTwoChar.value() == TemplateElementSplitter::LeftBrace;
                const auto nextIsTemplateLiteralEnd = atomCharStartIndex + 2 == templateStringEnd;
                if (nextTwoIsDollarLeftBrace || nextIsTemplateLiteralEnd)
                {
                    elementEnd = atomCharStartIndex + 1;
                    if (nextIsTemplateLiteralEnd)
                        findTail = true;
                    templateElementCollection.push_back(formartTemplateElement(elementStart, elementEnd, tempVale, nextIsTemplateLiteralEnd));
                    tempVale.clear();
                }
            }
        }
        if (!findTail)
            templateElementCollection.push_back(formartTemplateElement(templateStringEnd - 1, templateStringEnd - 1, "", true));

        return templateElementCollection;
    }

    const std::string TemplateElement::TemplateElementType = "TemplateElement";
};
